package backend.walter;

import java.util.*;

import com.dp.client.dto.Fecha;

import backend.Algoritmos.GeneradorDeRutas;
import backend.Algoritmos.Solver;
import backend.estructuras.*;
import backend.estructuras.common.*;



public class WSolver extends Solver {
	
	final static String NOMBRE = "Algoritmo probabilistico de Walter";
	
	public WSolver() {
		super(NOMBRE);
	}
	@Override
	protected Vector<Ruta> generaRutasDePaquete(Paquete paquete) {
		Almacen origen = paquete.getOrigen();
		Almacen destino = paquete.getDestino();
		Vector<Ruta> posiblesRutas = new Vector<>();
		Fecha fechaRegistro = paquete.getFechaRegistro();
		if(origen.getContinente().getNombre().equals(destino.getContinente().getNombre())){
			List<Almacen> almacenes = origen.getContinente().getAlmacenes();
			//directo con maximo 12 horas vacias
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasDirectas(paquete, origen, destino, fechaRegistro, new Fecha(0, 12)));
			//una escala con 0 horas vacias
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConUnaEscala(paquete, origen, destino, fechaRegistro, new Fecha(0, 0), almacenes));
		}else{
			List<Almacen> almacenesOrigen = origen.getContinente().getAlmacenes();
			List<Almacen> almacenesDestino = destino.getContinente().getAlmacenes();
			//un vuelo directo con maximo 24 horas vacias
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasDirectas(paquete, origen, destino, fechaRegistro, new Fecha(1, 0)));
			//un vuelo intercon + 2 intra con 0 horas vacias
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConDosEscalas(paquete, origen, destino, fechaRegistro, new Fecha(0, 0), almacenesDestino, almacenesDestino));
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConDosEscalas(paquete, origen, destino, fechaRegistro, new Fecha(0, 0), almacenesOrigen, almacenesDestino));
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConDosEscalas(paquete, origen, destino, fechaRegistro, new Fecha(0, 0), almacenesOrigen, almacenesOrigen));
			//un vuelo inter + 1 intra con maximo 12 horas vacias
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConUnaEscala(paquete, origen, destino, fechaRegistro, new Fecha(0, 12), almacenesOrigen));
			posiblesRutas.addAll(GeneradorDeRutas.generaRutasConUnaEscala(paquete, origen, destino, fechaRegistro, new Fecha(0, 12), almacenesDestino));
		}
		Vector<Ruta> output = new Vector<Ruta>();
		for(Ruta r : posiblesRutas)
			if(r.getBottleNeck() > 0) output.add(r);
		Collections.sort(output);
		return output;
	}
	@Override
	protected Ruta generaRutaDePaquete(Paquete paquete) {
		Vector<Ruta> posiblesRutas = generaRutasDePaquete(paquete);
		if(posiblesRutas.isEmpty() || posiblesRutas.firstElement().getBottleNeck() == 0) return null;
		else{
			Ruta ruta = posiblesRutas.firstElement();
			ruta.ejecutaRuta();
			return ruta;
		}
	}

	@Override
	public List<Paquete> generaPaquetesEstimadosParaIntervaloDeTiempo(Fecha fechaInicio, Fecha fechaFin){
		//si no hay distribuciones, retornamos null
		if(distribuciones.size() != almacenes.size() * (almacenes.size() - 1)) return null;
		
		LinkedList<Paquete> agregados = new LinkedList<Paquete>();
		for(Almacen a: this.almacenes)
			for(Almacen b : this.almacenes)
				if(a != b)
					for(Fecha f = new Fecha( fechaInicio.getDia() , fechaInicio.getHora() ); f.compareTo(fechaFin) < 0; f = Fecha.nextHora(f)){
						Distribucion distribucion = distribuciones.get(new AbstractMap.SimpleEntry<Almacen, Almacen>(a, b));
						int paquetes = distribucion.getRandom(f.getDiaSemana(), f.getHora());
						while(paquetes-- > 0){
							Paquete p = new Paquete(a, b, f, false);
							addPaqueteEstimado(p);
							agregados.add(p);
					}
		}
		return agregados;
	}
	


	@Override
	public void generaYEjecutaRutasParaIntervaloDeTiempo(Fecha fechaInicio, Fecha fechaFin) {
		Vector<Paquete> paquetesOrdenados = this.getPaquetesEnIntervaloDeTiempo(fechaInicio, fechaFin);
		Collections.sort( paquetesOrdenados );
		Paquete[] paquetes = new Paquete[paquetesOrdenados.size()];
		paquetesOrdenados.toArray(paquetes);
		//del ultimo al primero
		for(int i = paquetes.length - 1; i >= 0; i--){
			Paquete paquete = paquetes[i];
			if(this.generaRutaDePaquete(paquete) == null){
				this.addPaqueteSinRuta(paquete);
			}
		}
		System.out.println( "# paquetes que llegaron a su destino: " + this.paquetesSinRuta.size() );
		System.out.println( "# paquetes en total: " + paquetes.length );
	}

	@Override
	public void addDistribucion(Almacen origen, Almacen destino, int media, double varianza) {
		distribuciones.put(new AbstractMap.SimpleEntry<Almacen, Almacen>(origen, destino),
				new DistribucionNormalConexion(origen, destino, media, varianza));
	}
	
}
